package com.zy.explore.license.infrastructure.base.mybatis;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.zy.explore.license.infrastructure.util.DataBeanUtil;
import com.zy.explore.license.infrastructure.util.StrUtil;
import com.zy.explore.license.interfaces.frm.SortFieldFrm;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Sort;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * description 标准mybatis-plus EntityFrm，Mybatis框架持久层基类，继承它的实体类可使用mybatis-plus的CRUD方式
 *
 * @author 🇨🇳中国印🇨🇳 zyexplorej2ee@sina.com
 * @date 2022年01月09日 17:09:23
 */
@Getter
@Setter
@MappedSuperclass
public abstract class MpBaseEntity<T extends MpBaseEntity<T, PK>, PK extends Serializable> {

    /**
     * 禁止从外部修改此变量或变量中的任何内容
     */
    @Nullable
    private static Map<Class<?>, MpBaseDao<?>> repositories;

    /**
     * 创建时间
     */
    @Column(name = "create_time", insertable = false, updatable = false, columnDefinition = "DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'")
    @TableField(fill = FieldFill.INSERT)
    protected Date createTime;

    /**
     * 修改时间
     */
    @Column(name = "update_time", insertable = false, updatable = false, columnDefinition = "DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间'")
    @TableField(fill = FieldFill.UPDATE)
    protected Date updateTime;

    /**
     * 让对象自身具备保存功能
     *
     * @return 成功返回保存后的对象，失败返回null
     */
    public T save() {
        @SuppressWarnings("unchecked")
        MpBaseDao<T> repository = getRepository(MpBaseDao.class, getClass());
        @SuppressWarnings("unchecked")
        T entity = (T) this;
        int result = repository.insert(entity);
        if (result == 1) {
            return entity;
        }
        return null;
    }

    /**
     * 让对象自身具备findById功能
     *
     * @return 返回查找到的对象，可能为null
     */
    public T findById() {
        @SuppressWarnings("unchecked")
        MpBaseDao<T> repository = getRepository(MpBaseDao.class, getClass());
        @SuppressWarnings("unchecked")
        T entity = (T) this;
        return repository.selectById(entity.getId());
    }

    /**
     * 让对象自身具备findByIds功能
     *
     * @param ids 主键集合
     * @return 返回查找到的对象集合
     */
    public List<T> findByIds(List<PK> ids) {
        @SuppressWarnings("unchecked")
        MpBaseDao<T> repository = getRepository(MpBaseDao.class, getClass());
        return repository.selectBatchIds(ids);
    }

    /**
     * 让对象自身具备updateSelective功能
     *
     * @return 成功返回更新后的对象，失败返回null
     */
    public T updateSelective() {
        @SuppressWarnings("unchecked")
        MpBaseDao<T> repository = getRepository(MpBaseDao.class, getClass());
        PK id = getId();
        Assert.notNull(id, "id must not be null");
        T entity = repository.selectById(id);
        if (entity == null) {
            return null;
        }
        DataBeanUtil.copyProperties(this, entity, true);
        int result = repository.updateById(entity);
        if (result == 1) {
            return entity;
        }
        return null;
    }

    /**
     * 让对象自身具备findList返回集合的功能
     *
     * @param wrapper 查询参数对象
     * @return 返回查找到的对象集合
     */
    public List<T> findList(Wrapper<T> wrapper) {
        @SuppressWarnings("unchecked")
        MpBaseDao<T> repository = getRepository(MpBaseDao.class, getClass());
        return repository.selectList(wrapper);
    }

    /**
     * 让对象自身具备findPageInfo返回分页的功能
     *
     * @param page     分页查询参数
     * @param wrapper  查询参数对象
     * @return 返回查找到的对象分页
     */
    public Page findPageInfo(Page page, Wrapper wrapper) {
        @SuppressWarnings("unchecked")
        MpBaseDao repository = getRepository(MpBaseDao.class, getClass());
        val p = new com.baomidou.mybatisplus.extension.plugins.pagination.Page(page.getNumber(), page.getSize());
        IPage retPage = repository.selectPage(p, wrapper);
        return new PageImpl(retPage.getRecords(), page.getPageable(), retPage.getTotal());
    }

    /**
     * 让对象自身具备remove功能
     *
     * @return 删除成功返回1
     */
    @SuppressWarnings("unchecked")
    public int remove() {
        @SuppressWarnings("unchecked")
        MpBaseDao<T> repository = getRepository(MpBaseDao.class, getClass());
        return repository.deleteById(this.getId());
    }

    /**
     * 获取主键
     *
     * @return {@link PK}
     */
    public abstract PK getId();

    /**
     * 获取对应Entity的Repository
     *
     * @param repositoryType Repository的interface类型
     * @param entityType     Entity实现类型
     * @param <R>            返回Repository类型
     * @param <T>            Entity类型
     * @param <PK>           Entity主键类型
     * @return 此Entity对应的Repository
     * @throws IllegalStateException 对应Entity的repository不存在，或者{@link #repositories}从未初始化
     * @throws ClassCastException    对应Entity的repository并非类型{@code repositoryType}
     */
    protected static <R extends MpBaseDao<T>, T extends MpBaseEntity<T, PK>, PK extends Serializable>
    R getRepository(Class<R> repositoryType, Class<T> entityType) {
        Map<Class<?>, MpBaseDao<?>> repositories = MpBaseEntity.repositories;

        if (repositories == null) {
            throw new IllegalStateException("Repository map does not set");
        }

        MpBaseDao<?> repository = repositories.get(entityType);

        if (repository == null) {
            throw new IllegalStateException("Repository does not exists");
        }

        return repositoryType.cast(repository);
    }

    /**
     * 注意：此方法仅用于初始化Entity到Repository的映射，不允许任何外部调用
     *
     * @param repositories 初始化映射
     */
    public static void initRepositories(Map<Class<?>, MpBaseDao<?>> repositories) {
        Assert.notNull(repositories, "repositories must not be null");

        MpBaseEntity.repositories = new HashMap<>(repositories);
    }

    /**
     * 设置排序条件 sortFields集合有值则以该集合为主，为空则以设置的默认列和升降序排序，如若也不设置默认排序列，则以创建时间降序排序
     *
     * @param sortFields        排序参数对象集合
     * @param wrapper           条件包装对象
     * @param defaultSortColumn 默认排序字段
     * @param isAsc             是否升序 true:是 false:否
     */
    public static void setSortCriteria(List<SortFieldFrm> sortFields, QueryWrapper<?> wrapper, String defaultSortColumn, Boolean isAsc) {
        // 排序
        if (!CollectionUtils.isEmpty(sortFields)) {
            sortFields.forEach(sortField -> {
                if (Sort.Direction.ASC.name().equalsIgnoreCase(sortField.getOrder())) {
                    wrapper.orderByAsc(StrUtil.fieldToColumn(sortField.getPropName()));
                } else if (Sort.Direction.DESC.name().equalsIgnoreCase(sortField.getOrder())) {
                    wrapper.orderByDesc(StrUtil.fieldToColumn(sortField.getPropName()));
                }
            });
        } else {
            if (!StringUtils.isEmpty(defaultSortColumn)) {
                if (isAsc != null && isAsc) {
                    wrapper.orderByAsc(defaultSortColumn);
                } else {
                    wrapper.orderByDesc(defaultSortColumn);
                }
            } else {
                // 默认按创建时间降序排序
                wrapper.orderByDesc("create_time");
            }
        }
    }
}
